﻿/********************************************************
 * Module Name    : Workflow
 * Purpose        : 
 * Class Used     : X_AD_WF_Activity
 * Chronological Development
 * Veena Pandey     02-May-2009
 ******************************************************/

using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using VAdvantage.Classes;
using VAdvantage.Model;
using VAdvantage.Process;
using VAdvantage.DataBase;
using VAdvantage.Logging;
using VAdvantage.Utility;
using VAdvantage.Print;
using System.Threading;
using VAdvantage.ProcessEngine;
//using VAdvantage.ProcessEngine;
namespace VAdvantage.WF
{
    public class MWFActivity : X_AD_WF_Activity
    {
        /**	State Machine				*/
        private StateEngine _state = null;
        /**	Workflow Node				*/
        private MWFNode _node = null;
        /** Transaction					*/
        private Trx _trx = null;
        /**	Audit						*/
        private MWFEventAudit _audit = null;
        /** Persistent Object			*/
        private PO _po = null;
        /** Document Status				*/
        private String _docStatus = null;
        /**	New Value to save in audit	*/
        private String _newValue = null;
        /** Process						*/
        private MWFProcess _process = null;
        /** Post Immediate Candidate	*/
        private DocAction _postImmediate = null;
        /** List of email recipients	*/
        private List<String> _emails = new List<String>();

        /**	Static Logger	*/
        private static VLogger _log = VLogger.GetVLogger(typeof(MWFActivity).FullName);

        /// <summary>
        /// Standard Constructor
        /// </summary>
        /// <param name="ctx">context</param>
        /// <param name="AD_WF_Activity_ID">id</param>
        /// <param name="trxName">transaction</param>
        public MWFActivity(Ctx ctx, int AD_WF_Activity_ID, String trxName)
            : base(ctx, AD_WF_Activity_ID, trxName)
        {
            if (AD_WF_Activity_ID == 0)
                throw new ArgumentException("Cannot create new WF Activity directly");
            _state = new StateEngine(GetWFState());
        }

        /// <summary>
        /// Load Constructor
        /// </summary>
        /// <param name="ctx">context</param>
        /// <param name="rs">result set</param>
        /// <param name="trxName">transaction</param>
        public MWFActivity(Ctx ctx, DataRow rs, String trxName)
            : base(ctx, rs, trxName)
        {
            _state = new StateEngine(GetWFState());
        }

        /// <summary>
        /// Parent Contructor
        /// </summary>
        /// <param name="process">process</param>
        /// <param name="AD_WF_Node_ID">start node id</param>
        public MWFActivity(MWFProcess process, int AD_WF_Node_ID)
            : base(process.GetCtx(), 0, process.Get_TrxName())
        {
            SetAD_WF_Process_ID(process.GetAD_WF_Process_ID());
            SetPriority(process.GetPriority());
            //	Document Link
            SetAD_Table_ID(process.GetAD_Table_ID());
            SetRecord_ID(process.GetRecord_ID());
            //	Status
            base.SetWFState(WFSTATE_NotStarted);
            _state = new StateEngine(GetWFState());
            SetProcessed(false);
            //	Set Workflow Node
            SetAD_Workflow_ID(process.GetAD_Workflow_ID());
            SetAD_WF_Node_ID(AD_WF_Node_ID);
            //	Node Priority & End Duration
            MWFNode node = MWFNode.Get(GetCtx(), AD_WF_Node_ID);
            int priority = node.GetPriority();
            if (priority != 0 && priority != GetPriority())
                SetPriority(priority);
            long limitMS = node.GetDurationLimitMS();
            if (limitMS != 0)
            {
                //SetEndWaitTime(new DateTime(limitMS + CommonFunctions.CurrentTimeMillis())); // not gives correct output
                SetEndWaitTime(DateTime.Now.AddMilliseconds(limitMS));
            }
            //	Responsible
            SetResponsible(process);
            Save();
            //
            _audit = new MWFEventAudit(this);
            _audit.Save();
            //
            _process = process;
        }


        /// <summary>
        /// Get Activities for table/tecord
        /// </summary>
        /// <param name="ctx">context</param>
        /// <param name="AD_Table_ID">table id</param>
        /// <param name="Record_ID">record id</param>
        /// <param name="activeOnly">if true only not processed records are returned</param>
        /// <returns>activity</returns>
        public static MWFActivity[] Get(Ctx ctx, int AD_Table_ID, int Record_ID, bool activeOnly)
        {
            List<MWFActivity> list = new List<MWFActivity>();
            String sql = "SELECT * FROM AD_WF_Activity WHERE AD_Table_ID=" + AD_Table_ID + " AND Record_ID=" + Record_ID + "";
            if (activeOnly)
                sql += " AND Processed<>'Y'";
            sql += " ORDER BY AD_WF_Activity_ID";
            try
            {
                DataSet ds = DataBase.DB.ExecuteDataset(sql, null, null);
                if (ds.Tables.Count > 0)
                {
                    foreach (DataRow dr in ds.Tables[0].Rows)
                    {
                        list.Add(new MWFActivity(ctx, dr, null));
                    }
                }
            }
            catch (Exception e)
            {
                _log.Log(Level.SEVERE, sql, e);
            }
            MWFActivity[] retValue = new MWFActivity[list.Count];
            retValue = list.ToArray();
            return retValue;
        }

        /// <summary>
        /// Get Active Info
        /// </summary>
        /// <param name="ctx">context</param>
        /// <param name="AD_Table_ID">table id</param>
        /// <param name="Record_ID">record id</param>
        /// <returns>activity summary</returns>
        public static String GetActiveInfo(Ctx ctx, int AD_Table_ID, int Record_ID)
        {
            MWFActivity[] acts = Get(ctx, AD_Table_ID, Record_ID, true);
            if (acts == null || acts.Length == 0)
                return null;
            //
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < acts.Length; i++)
            {
                if (i > 0)
                    sb.Append("\n");
                MWFActivity activity = acts[i];
                sb.Append(activity.ToStringX());
            }
            return sb.ToString();
        }

        /// <summary>
        /// Get State
        /// </summary>
        /// <returns>state</returns>
        public StateEngine GetState()
        {
            return _state;
        }

        /// <summary>
        /// Set Activity State
        /// </summary>
        /// <param name="WFState">WFState</param>
        public void SetWFState(String WFState)
        {
            if (_state == null)
                _state = new StateEngine(GetWFState());
            if (_state.IsClosed())
                return;
            if (GetWFState().Equals(WFState))
                return;
            //
            if (_state.IsValidNewState(WFState))
            {
                String oldState = GetWFState();
                log.Fine(oldState + "->" + WFState + ", Msg=" + GetTextMsg());
                base.SetWFState(WFState);
                _state = new StateEngine(GetWFState());
                Save();			//	closed in MWFProcess.checkActivities()
                UpdateEventAudit();

                //	Inform Process
                if (_process == null)
                    _process = new MWFProcess(GetCtx(), GetAD_WF_Process_ID(), null);
                _process.CheckActivities();
            }
            else
            {
                String msg = "Ignored Invalid Transformation - New="
                    + WFState + ", Current=" + GetWFState();
                log.Log(Level.SEVERE, msg);
                //Trace.printStack();
                SetTextMsg("Set WFState - " + msg);
                Save();
            }
        }

        /// <summary>
        /// Is Activity closed
        /// </summary>
        /// <returns>true if closed</returns>
        public bool IsClosed()
        {
            return _state.IsClosed();
        }

        /// <summary>
        /// Update Event Audit
        /// </summary>
        private void UpdateEventAudit()
        {
            //	//log.fine("");
            GetEventAudit();
            _audit.SetTextMsg(GetTextMsg());
            _audit.SetWFState(GetWFState());
            if (_newValue != null)
                _audit.SetNewValue(_newValue);
            if (_state.IsClosed())
            {
                _audit.SetEventType(MWFEventAudit.EVENTTYPE_ProcessCompleted);
                long ms = CommonFunctions.CurrentTimeMillis();//-_audit.GetCreated().getTime();
                _audit.SetElapsedTimeMS(Convert.ToDecimal(ms));

            }
            else
                _audit.SetEventType(MWFEventAudit.EVENTTYPE_StateChanged);
            _audit.Save();
        }

        /// <summary>
        /// Get/Create Event Audit
        /// </summary>
        /// <returns>event</returns>
        public MWFEventAudit GetEventAudit()
        {
            if (_audit != null)
                return _audit;
            MWFEventAudit[] events = MWFEventAudit.Get(GetCtx(), GetAD_WF_Process_ID(), GetAD_WF_Node_ID());
            if (events == null || events.Length == 0)
                _audit = new MWFEventAudit(this);
            else
                _audit = events[events.Length - 1];		//	last event
            return _audit;
        }

        /// <summary>
        /// Get Persistent Object in Transaction
        /// </summary>
        /// <param name="trxName">transaction</param>
        /// <returns>po</returns>
        public PO GetPO(String trxName)
        {
            if (_po != null)
                return _po;

            MTable table = MTable.Get(GetCtx(), GetAD_Table_ID());
            _po = table.GetPO(GetCtx(), GetRecord_ID(), trxName);
            return _po;
        }

        /// <summary>
        /// Get Persistent Object in Transaction 
        /// </summary>
        /// <param name="trx">transaction</param>
        /// <returns>po</returns>
        public PO GetPO(Trx trx)
        {
            if (_po != null)
                return _po;
            if (trx == null)
                return GetPO((String)null);
            return GetPO(trx.GetTrxName());
        }

        /// <summary>
        /// Get Persistent Object.
        /// </summary>
        /// <returns>po</returns>
        public PO GetPO()
        {
            return GetPO(_trx);
        }

        /// <summary>
        /// Get PO AD_Client_ID
        /// </summary>
        /// <param name="trxName">transaction</param>
        /// <returns>client of PO or -1</returns>
        public int GetPO_AD_Client_ID(String trxName)
        {
            if (_po == null && trxName == null)
                GetPO(_trx);
            if (_po == null)
                GetPO(trxName);
            if (_po != null)
                return _po.GetAD_Client_ID();
            return -1;
        }

        /// <summary>
        /// Get Attribute Value (based on Node) of PO
        /// </summary>
        /// <returns>Attribute Value or null</returns>
        public Object GetAttributeValue()
        {
            MWFNode node = GetNode();
            if (node == null)
                return null;
            int AD_Column_ID = node.GetAD_Column_ID();
            if (AD_Column_ID == 0)
                return null;
            PO po = GetPO();
            if (po.Get_ID() == 0)
                return null;
            return po.Get_ValueOfColumn(AD_Column_ID);
        }

        /// <summary>
        /// Is SO Trx
        /// </summary>
        /// <returns>SO Trx or of not found true</returns>
        public bool IsSOTrx()
        {
            PO po = GetPO();
            if (po.Get_ID() == 0)
                return true;
            //	Is there a Column?
            int index = po.Get_ColumnIndex("IsSOTrx");
            if (index < 0)
            {
                if (po.Get_TableName().StartsWith("M_"))
                    return false;
                return true;
            }
            //	we have a column
            try
            {
                bool IsSOTrx = (bool)po.Get_Value(index);
                return IsSOTrx;
            }
            catch (Exception e)
            {
                log.Log(Level.SEVERE, "", e);
            }
            return true;
        }

        /// <summary>
        /// Set AD_WF_Node_ID.
        /// (Re)Set to Not Started
        /// </summary>
        /// <param name="AD_WF_Node_ID">new node</param>
        public void SetAD_WF_Node_ID(int AD_WF_Node_ID)
        {
            if (AD_WF_Node_ID == 0)
                throw new ArgumentException("Workflow Node is not defined");
            base.SetAD_WF_Node_ID(AD_WF_Node_ID);
            //
            if (!WFSTATE_NotStarted.Equals(GetWFState()))
            {
                base.SetWFState(WFSTATE_NotStarted);
                _state = new StateEngine(GetWFState());
            }
            if (IsProcessed())
                SetProcessed(false);
        }

        /// <summary>
        /// Get WF Node
        /// </summary>
        /// <returns>node</returns>
        public MWFNode GetNode()
        {
            if (_node == null)
                _node = MWFNode.Get(GetCtx(), GetAD_WF_Node_ID());
            return _node;
        }

        /// <summary>
        /// Get WF Node Name
        /// </summary>
        /// <returns>translated node name</returns>
        public String GetNodeName()
        {
            return GetNode().GetName(true);
        }

        /// <summary>
        /// Get Node Description
        /// </summary>
        /// <returns>translated node description</returns>
        public String GetNodeDescription()
        {
            return GetNode().GetDescription(true);
        }

        /// <summary>
        /// Get Node Help
        /// </summary>
        /// <returns>translated node help</returns>
        public String GetNodeHelp()
        {
            return GetNode().GetHelp(true);
        }

        /// <summary>
        /// Is this an user Approval step?
        /// </summary>
        /// <returns>true if User Approval</returns>
        public bool IsUserApproval()
        {
            return GetNode().IsUserApproval();
        }

        /// <summary>
        /// Is this a Manual user step?
        /// </summary>
        /// <returns>true if Window/Form/..</returns>
        public bool IsUserManual()
        {
            return GetNode().IsUserManual();
        }

        /// <summary>
        /// Is this a user choice step?
        /// </summary>
        /// <returns>true if User Choice</returns>
        public bool IsUserChoice()
        {
            return GetNode().IsUserChoice();
        }

        /// <summary>
        /// Set Text Msg (add to existing)
        /// </summary>
        /// <param name="textMsg">msg</param>
        public void SetTextMsg(String textMsg)
        {
            if (textMsg == null || textMsg.Length == 0)
                return;
            String oldText = GetTextMsg();
            if (oldText == null || oldText.Length == 0)
            {
                //base.SetTextMsg(Utility.trimSize(textMsg, 1000));
                base.SetTextMsg(textMsg);
            }
            else if (textMsg != null && textMsg.Length > 0)
            {
                //base.SetTextMsg(Utility.trimSize(oldText + "\n - " + textMsg, 1000));
                base.SetTextMsg(oldText + "\n - " + textMsg);
            }
        }

        /// <summary>
        /// Add to Text Msg
        /// </summary>
        /// <param name="obj">some object</param>
        public void AddTextMsg(Object obj)
        {
            if (obj == null)
                return;
            //
            StringBuilder textMsg = new StringBuilder(obj.ToString());
            //if (obj instanceof Exception)
            if (obj.GetType() == typeof(Exception))
            {
                Exception ex = (Exception)obj;
                //while (ex != null)
                //{
                //    StackTraceElement[] st = ex.getStackTrace();
                //    for (int i = 0; i < st.Length; i++)
                //    {
                //        StackTraceElement ste = st[i];
                //        if (i == 0 || ste.getClassName().startsWith("org.compiere"))
                //            TextMsg.append(" (").append(i).append("): ")
                //                .append(ste.toString())
                //                .append("\n");
                //    }
                //    if (ex.getCause() instanceof Exception)
                //        ex = (Exception)ex.getCause();
                //    else
                //        ex = null;
                //}
            }
            //
            String oldText = GetTextMsg();
            if (oldText == null || oldText.Length == 0)
            {
                //base.SetTextMsg(Utility.trimSize(TextMsg.toString(),1000));
                base.SetTextMsg(textMsg.ToString());
            }
            else if (textMsg != null && textMsg.Length > 0)
            {
                //base.SetTextMsg(Utility.trimSize(oldText + "\n - " + textMsg.ToString(),1000));
                base.SetTextMsg(oldText + "\n - " + textMsg.ToString());
            }
        }

        /// <summary>
        /// Get WF State text
        /// </summary>
        /// <returns>state text</returns>
        public String GetWFStateText()
        {
            return MRefList.GetListName(GetCtx(), WFSTATE_AD_Reference_ID, GetWFState());
        }

        /// <summary>
        /// Set Responsible and User from Process / Node
        /// </summary>
        /// <param name="process">process</param>
        private void SetResponsible(MWFProcess process)
        {
            //	Responsible
            int AD_WF_Responsible_ID = GetNode().GetAD_WF_Responsible_ID();
            if (AD_WF_Responsible_ID == 0)	//	not defined on Node Level
                AD_WF_Responsible_ID = process.GetAD_WF_Responsible_ID();
            SetAD_WF_Responsible_ID(AD_WF_Responsible_ID);
            MWFResponsible resp = GetResponsible();

            //	User - Directly responsible
            int AD_User_ID = resp.GetAD_User_ID();
            //	Invoker - get Sales Rep or last updater of document
            if (AD_User_ID == 0 && resp.IsInvoker())
                AD_User_ID = process.GetAD_User_ID();
            //
            SetAD_User_ID(AD_User_ID);
        }

        /// <summary>
        /// Get Responsible
        /// </summary>
        /// <returns>responsible</returns>
        public MWFResponsible GetResponsible()
        {
            MWFResponsible resp = MWFResponsible.Get(GetCtx(), GetAD_WF_Responsible_ID());
            return resp;
        }

        /// <summary>
        /// Is Invoker (no user & no role)
        /// </summary>
        /// <returns>true if invoker</returns>
        public bool IsInvoker()
        {
            return GetResponsible().IsInvoker();
        }

        /// <summary>
        /// Get Approval User.
        /// If the returned user is the same, the document is approved.
        /// </summary>
        /// <param name="AD_User_ID">starting User</param>
        /// <param name="C_Currency_ID">currency</param>
        /// <param name="amount">amount</param>
        /// <param name="AD_Org_ID">document organization</param>
        /// <param name="ownDocument">the document is owned by AD_User_ID</param>
        /// <returns>AD_User_ID - if -1 no Approver</returns>
        public int GetApprovalUser(int AD_User_ID, int C_Currency_ID, Decimal amount,
            int AD_Org_ID, bool ownDocument)
        {
            //	Nothing to approve
            if (amount == null || Math.Sign(amount) == 0)
                return AD_User_ID;

            //	Starting user
            MUser user = MUser.Get(GetCtx(), AD_User_ID);
            log.Info("For User=" + user + ", Amt=" + amount + ", Own=" + ownDocument);

            MUser oldUser = null;
            while (user != null)
            {
                if (user.Equals(oldUser))
                {
                    log.Info("Loop - " + user.GetName());
                    return -1;
                }
                oldUser = user;
                log.Fine("User=" + user.GetName());
                //	Get Roles of User
                MRole[] roles = user.GetRoles(AD_Org_ID);
                for (int i = 0; i < roles.Length; i++)
                {
                    MRole role = roles[i];
                    if (ownDocument && !role.IsCanApproveOwnDoc())
                        continue;	//	find a role with allows them to approve own
                    Decimal roleAmt = role.GetAmtApproval();
                    if (roleAmt == null || Math.Sign(roleAmt) == 0)
                        continue;
                    if (C_Currency_ID != role.GetC_Currency_ID()
                        && role.GetC_Currency_ID() != 0)			//	No currency = amt only
                    {
                        roleAmt = MConversionRate.Convert(GetCtx(),//	today & default rate 
                            roleAmt, role.GetC_Currency_ID(),
                            C_Currency_ID, GetAD_Client_ID(), AD_Org_ID);
                        if (roleAmt == null || Math.Sign(roleAmt) == 0)
                            continue;
                    }
                    bool approved = amount.CompareTo(roleAmt) <= 0;
                    log.Fine("Approved=" + approved + " - User=" + user.GetName() + ", Role=" + role.GetName()
                        + ", ApprovalAmt=" + roleAmt);
                    if (approved)
                        return user.GetAD_User_ID();
                }

                //	**** Find next User 
                //	Get Supervisor
                if (user.GetSupervisor_ID() != 0)
                {
                    user = MUser.Get(GetCtx(), user.GetSupervisor_ID());
                    log.Fine("Supervisor: " + user.GetName());
                }
                else
                {
                    log.Fine("No Supervisor");
                    MOrg org = MOrg.Get(GetCtx(), AD_Org_ID);
                    MOrgInfo orgInfo = org.GetInfo();
                    //	Get Org Supervisor
                    if (orgInfo.GetSupervisor_ID() != 0)
                    {
                        user = MUser.Get(GetCtx(), orgInfo.GetSupervisor_ID());
                        log.Fine("Org=" + org.GetName() + ",Supervisor: " + user.GetName());
                    }
                    else
                    {
                        log.Fine("No Org Supervisor");
                        //	Get Parent Org Supervisor
                        if (orgInfo.GetParent_Org_ID() != 0)
                        {
                            org = MOrg.Get(GetCtx(), orgInfo.GetParent_Org_ID());
                            orgInfo = org.GetInfo();
                            if (orgInfo.GetSupervisor_ID() != 0)
                            {
                                user = MUser.Get(GetCtx(), orgInfo.GetSupervisor_ID());
                                log.Fine("Parent Org Supervisor: " + user.GetName());
                            }
                        }
                    }
                }	//	No Supervisor

            }	//	while there is a user to approve

            log.Fine("No user found");
            return -1;
        }

        /// <summary>
        /// Execute Work.
        /// Called from MWFProcess.StartNext
        /// Feedback to Process via SetWFState -> CheckActivities
        /// </summary>
        public void Run()
        {
            log.Info(ToString());
            _newValue = null;
            if (!_state.IsValidAction(StateEngine.ACTION_START))
            {
                SetTextMsg("State=" + GetWFState() + " - cannot start");
                SetWFState(StateEngine.STATE_TERMINATED);
                return;
            }
            //
            SetWFState(StateEngine.STATE_RUNNING);
            _trx = Trx.Get(Trx.CreateTrxName("WF"), true);

            //
            try
            {
                if (GetNode().Get_ID() == 0)
                {
                    SetTextMsg("Node not found - AD_WF_Node_ID=" + GetAD_WF_Node_ID());
                    SetWFState(StateEngine.STATE_ABORTED);
                    return;
                }
                //	Do Work
                /****	Trx Start	****/
                //	log.config("*Start " + toString() + " - " + _trx.getTrxName());
                bool done = PerformWork(_trx);
                /****	Trx End		****/
                //	log.config("*Commit " + toString() + " - " + _trx.getTrxName());
                _trx.Commit();
                _trx.Close();
                _trx = null;
                //
                //	log.config("*State " + toString());
                SetWFState(done ? StateEngine.STATE_COMPLETED : StateEngine.STATE_SUSPENDED);
                //	log.config("*Done  " + toString());
                //
                if (_postImmediate != null)
                {
                    PostImmediate();
                }
            }
            catch (Exception e)
            {
                log.Log(Level.WARNING, "" + GetNode(), e);
                /****	Trx Rollback	****/
                _trx.Rollback();
                _trx.Close();
                _trx = null;
                //
                if (e.Message != null)
                {
                    log.Log(Level.WARNING, "Cause", e.Message);
                }
                String processMsg = e.Message;
                //if (processMsg == null || processMsg.Length == 0)
                //    processMsg = e.getMessage();
                SetTextMsg(processMsg);
                AddTextMsg(e);
                SetWFState(StateEngine.STATE_TERMINATED);	//	unlocks
                //	Set Document Status 
                if (_po != null && _docStatus != null)
                {
                    _po.Load((String)null);
                    DocAction doc = (DocAction)_po;
                    doc.SetDocStatus(_docStatus);
                    _po.Save();
                }
            }
            _trx = null;
        }

        /// <summary>
        /// Perform Work. Set Text Msg.
        /// </summary>
        /// <param name="trx">transaction</param>
        /// <returns>true if completed, false otherwise and throws Exception if error</returns>
        private bool PerformWork(Trx trx)
        {
            log.Info(_node + " [" + trx.GetTrxName() + "]");
            _postImmediate = null;
            _docStatus = null;
            if (_node.GetPriority() != 0)		//	overwrite priority if defined
                SetPriority(_node.GetPriority());
            String action = _node.GetAction();

            /******	Sleep (Start/End)			******/
            if (MWFNode.ACTION_WaitSleep.Equals(action))
            {
                log.Fine("Sleep:WaitTime=" + _node.GetWaitTime());
                if (_node.GetWaitingTime() == 0)
                    return true;	//	done

                //java.util.Calendar cal = java.util.Calendar.getInstance();
                //cal.add(_node.GetDurationCalendarField(), _node.GetWaitTime());
                //SetEndWaitTime(new Timestamp(cal.getTimeInMillis()));

                DateTime dtTime = CommonFunctions.AddDate(_node.GetDurationCalendarField(), _node.GetWaitTime());
                SetEndWaitTime(dtTime);
                return false;		//	not done
            }

            /******	Document Action				******/
            else if (MWFNode.ACTION_DocumentAction.Equals(action))
            {
                log.Fine("DocumentAction=" + _node.GetDocAction());
                GetPO(trx);
                if (_po == null)
                    throw new Exception("Persistent Object not found - AD_Table_ID="
                        + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID());
                _po.Set_TrxName(trx.GetTrxName());
                bool success = false;
                String processMsg = null;
                DocAction doc = null;
                if (_po.GetType() == typeof(DocAction) || _po.GetType().GetInterface("DocAction", true) == typeof(DocAction))
                {
                    doc = (DocAction)_po;
                    //
                    success = doc.ProcessIt(_node.GetDocAction());	//	MOrder ** Do the work
                    SetTextMsg(doc.GetSummary());
                    processMsg = doc.GetProcessMsg();
                    _docStatus = doc.GetDocStatus();
                    //	Post Immediate
                    if (success && DocActionVariables.ACTION_COMPLETE.Equals(_node.GetDocAction()))
                    {
                        MClient client = MClient.Get(doc.GetCtx(), doc.GetAD_Client_ID());
                        if (client.IsPostImmediate())
                            _postImmediate = doc;
                    }
                    //
                    if (_process != null)
                        _process.SetProcessMsg(processMsg);
                }
                else
                {
                    //throw new IllegalStateException("Persistent Object not DocAction - "
                    //    + _po.getClass().getName()
                    //    + " - AD_Table_ID=" + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID());
                    throw new Exception("Persistent Object not DocAction"
                        + " - AD_Table_ID=" + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID());
                }
                //
                if (!_po.Save())
                {
                    success = false;
                    processMsg = "SaveError";
                }
                if (!success)
                {
                    if (processMsg == null || processMsg.Length == 0)
                    {
                        processMsg = "PerformWork Error - " + _node.ToStringX();
                        if (doc != null)	//	problem: status will be rolled back
                            processMsg += " - DocStatus=" + doc.GetDocStatus();
                    }
                    throw new Exception(processMsg);
                }
                return success;
            }	//	DocumentAction

            /******	Report						******/
            else if (MWFNode.ACTION_AppsReport.Equals(action))
            {
                log.Fine("Report:AD_Process_ID=" + _node.GetAD_Process_ID());
                //	Process
                MProcess process = MProcess.Get(GetCtx(), _node.GetAD_Process_ID());
                if (!process.IsReport() || process.GetAD_ReportView_ID() == 0)
                {
                    //throw new IllegalStateException("Not a Report AD_Process_ID=" + _node.getAD_Process_ID());
                    throw new Exception("Not a Report AD_Process_ID=" + _node.GetAD_Process_ID());
                }
                //
                ProcessInfo pi = new ProcessInfo(_node.GetName(true), _node.GetAD_Process_ID(),
                    GetAD_Table_ID(), GetRecord_ID());
                pi.SetAD_User_ID(GetAD_User_ID());
                pi.SetAD_Client_ID(GetAD_Client_ID());
                MPInstance pInstance = new MPInstance(process, GetRecord_ID());
                FillParameter(pInstance, trx);
                pi.SetAD_PInstance_ID(pInstance.GetAD_PInstance_ID());
                //	Report
                //ReportEngine re = ReportEngine.get(GetCtx(), pi);
                //if (re == null)
                //{
                //    //throw new IllegalStateException("Cannot create Report AD_Process_ID=" + _node.getAD_Process_ID());
                //    throw new Exception("Cannot create Report AD_Process_ID=" + _node.GetAD_Process_ID());
                //}
                //File report = re.getPDF();

                ReportRun(pi);
                //	Notice
                int AD_Message_ID = 753;		//	HARDCODED WorkflowResult
                MNote note = new MNote(GetCtx(), AD_Message_ID, GetAD_User_ID(), trx.GetTrxName());
                note.SetTextMsg(_node.GetName(true));
                note.SetDescription(_node.GetDescription(true));
                note.SetRecord(GetAD_Table_ID(), GetRecord_ID());
                note.Save();
                ////	Attachment
                //MAttachment attachment = new MAttachment(GetCtx(), MNote.Table_ID, note.GetAD_Note_ID(), Get_TrxName());
                //attachment.AddEntry(report);
                //attachment.SetTextMsg(_node.GetName(true));
                //attachment.Save();
                return true;
            }

            /******	Process						******/
            else if (MWFNode.ACTION_AppsProcess.Equals(action))
            {
                log.Fine("Process:AD_Process_ID=" + _node.GetAD_Process_ID());
                //	Process
                MProcess process = MProcess.Get(GetCtx(), _node.GetAD_Process_ID());
                MPInstance pInstance = new MPInstance(process, GetRecord_ID());
                FillParameter(pInstance, trx);
                ////
                //ProcessInfo pi = new ProcessInfo(_node.GetName(true), _node.GetAD_Process_ID(),
                //    GetAD_Table_ID(), GetRecord_ID());
                //pi.SetAD_User_ID(GetAD_User_ID());
                //pi.SetAD_Client_ID(GetAD_Client_ID());
                //pi.SetAD_PInstance_ID(pInstance.GetAD_PInstance_ID());
                //return process.ProcessIt(pi, trx);

                //bool chk = true;

                var client = AEnv.GetDSPWFClient();
                client.ExecuteProcessCompleted += (se, ev) =>
                {
                    client.CloseAsync();

                    //chk = false;
                };
                client.ExecuteProcessAsync((Dictionary<string, string>)Envs.GetCtx().GetMap(),_node.GetAD_Process_ID(),process.GetName(),
                    pInstance.GetAD_PInstance_ID(),GetAD_Table_ID(),GetRecord_ID());
                return true;

            }

            /******	TODO Start Task				******/
            else if (MWFNode.ACTION_AppsTask.Equals(action))
            {
                log.Warning("Task:AD_Task_ID=" + _node.GetAD_Task_ID());
            }

            /******	EMail						******/
            else if (MWFNode.ACTION_EMail.Equals(action))
            {
                log.Fine("EMail:EMailRecipient=" + _node.GetEMailRecipient());
                GetPO(trx);
                if (_po == null)
                    throw new Exception("Persistent Object not found - AD_Table_ID="
                        + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID());
                if (_po.GetType() == typeof(DocAction) || _po.GetType().GetInterface("DocAction", true) == typeof(DocAction))
                {
                    _emails = new List<String>();
                    SendEMail();
                    StringBuilder sbEmail = new StringBuilder();
                    for (int i = 0; i < _emails.Count; i++)
                    {
                        if (i == 0)
                            sbEmail.Append(_emails[i]);
                        else
                            sbEmail.Append(", " + _emails[i]);
                    }
                    SetTextMsg(sbEmail.ToString());
                }
                else
                {
                    MClient client = MClient.Get(GetCtx(), GetAD_Client_ID());
                    MMailText mailtext = new MMailText(GetCtx(), GetNode().GetR_MailText_ID(), null);

                    String subject = GetNode().GetDescription()
                    + ": " + mailtext.GetMailHeader();

                    String message = mailtext.GetMailText(true)
                    + "\n-----\n" + GetNodeHelp();
                    String to = GetNode().GetEMail();

                    //client.SendEMail(to,GetNode().GetName(), subject, message, null);
                    client.SendEMail(client, client.GetEMailTest(), client.GetName(), to, GetNode().GetName(), subject, message, Envs.GetCtx());

                }



                return true;	//	done
            }	//	EMail

            /******	Set Variable				******/
            else if (MWFNode.ACTION_SetVariable.Equals(action))
            {
                String value = _node.GetAttributeValue();
                log.Fine("SetVariable:AD_Column_ID=" + _node.GetAD_Column_ID() + " to " + value);
                MColumn column = _node.GetColumn();
                int dt = column.GetAD_Reference_ID();
                return SetVariable(value, dt, null);
            }

            /******	TODO Start WF Instance		******/
            else if (MWFNode.ACTION_SubWorkflow.Equals(action))
            {
                log.Warning("Workflow:AD_Workflow_ID=" + _node.GetAD_Workflow_ID());
            }

            /******	User Choice					******/
            else if (MWFNode.ACTION_UserChoice.Equals(action))
            {
                log.Fine("UserChoice:AD_Column_ID=" + _node.GetAD_Column_ID());
                //	Approval
                if (_node.IsUserApproval() && (GetPO().GetType() == typeof(DocAction) || GetPO().GetType().GetInterface("DocAction", true) == typeof(DocAction)))
                {
                    DocAction doc = (DocAction)_po;
                    bool autoApproval = false;
                    //	Approval Hierarchy
                    if (IsInvoker())
                    {
                        //	Set Approver
                        int startAD_User_ID = GetAD_User_ID();
                        if (startAD_User_ID == 0)
                            startAD_User_ID = doc.GetDoc_User_ID();
                        int nextAD_User_ID = GetApprovalUser(startAD_User_ID,
                            doc.GetC_Currency_ID(), doc.GetApprovalAmt(),
                            doc.GetAD_Org_ID(),
                            startAD_User_ID == doc.GetDoc_User_ID());	//	own doc
                        //	same user = approved
                        autoApproval = startAD_User_ID == nextAD_User_ID;
                        if (!autoApproval)
                            SetAD_User_ID(nextAD_User_ID);
                    }
                    else	//	fixed Approver
                    {
                        MWFResponsible resp = GetResponsible();
                        autoApproval = resp.GetAD_User_ID() == GetAD_User_ID();
                        if (!autoApproval && resp.GetAD_User_ID() != 0)
                            SetAD_User_ID(resp.GetAD_User_ID());
                    }
                    if (autoApproval
                        && doc.ProcessIt(DocActionVariables.ACTION_APPROVE)
                        && doc.Save())
                        return true;	//	done
                }	//	approval
                return false;	//	wait for user
            }
            /******	User Workbench				******/
            else if (MWFNode.ACTION_UserWorkbench.Equals(action))
            {
                log.Fine("Workbench:?");
                return false;
            }
            /******	User Form					******/
            else if (MWFNode.ACTION_UserForm.Equals(action))
            {
                log.Fine("Form:AD_For_ID=" + _node.GetAD_Form_ID());
                return false;
            }
            /******	User Window					******/
            else if (MWFNode.ACTION_UserWindow.Equals(action))
            {
                log.Fine("Window:AD_Window_ID=" + _node.GetAD_Window_ID());
                return false;
            }
            /********** SendSms ***********/
            else if (MWFNode.ACTION_SMS.Equals(action))
            {
                GetPO(trx);
                if (_po == null)
                    throw new Exception("Persistent Object not found - AD_Table_ID="
                        + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID());
                SendSms();
                return true;
            }

            /************** FAX **********/
            else if (MWFNode.ACTION_FaxEMail.Equals(action))
            {
                //log.Fine("EMail:EMailRecipient=" + _node.GetEMailRecipient());
                GetPO(trx);
                if (_po == null)
                    throw new Exception("Persistent Object not found - AD_Table_ID="
                        + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID());
                if (_po.GetType() == typeof(DocAction) || _po.GetType().GetInterface("DocAction", true) == typeof(DocAction))
                {
                    _emails = new List<String>();
                    SendFaxEMail();
                    StringBuilder sbEmail = new StringBuilder();
                    for (int i = 0; i < _emails.Count; i++)
                    {
                        if (i == 0)
                            sbEmail.Append(_emails[i]);
                        else
                            sbEmail.Append(", " + _emails[i]);
                    }
                    SetTextMsg(sbEmail.ToString());
                }
                else
                {
                    MClient client = MClient.Get(GetCtx(), GetAD_Client_ID());
                    MMailText mailtext = new MMailText(GetCtx(), GetNode().GetR_MailText_ID(), null);

                    String subject = GetNode().GetDescription()
                    + ": " + mailtext.GetMailHeader();

                    String message = mailtext.GetMailText(true)
                    + "\n-----\n" + GetNodeHelp();
                    String to = GetNode().GetEMail();

                    //client.SendEMail(to,GetNode().GetName(), subject, message, null);
                    client.SendEMail(to, GetNode().GetName(), subject, message, null);

                }
                return true;
            }
            /************** EMail&FAX *********/
            else if (MWFNode.ACTION_EMailPlusFaxEMail.Equals(action))
            {
                ///////////Email//////////
                log.Fine("EMail:EMailRecipient=" + _node.GetEMailRecipient());
                GetPO(trx);
                if (_po == null)
                    throw new Exception("Persistent Object not found - AD_Table_ID="
                        + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID());
                if (_po.GetType() == typeof(DocAction) || _po.GetType().GetInterface("DocAction", true) == typeof(DocAction))
                {
                    _emails = new List<String>();
                    SendEMail();
                    StringBuilder sbEmail = new StringBuilder();
                    for (int i = 0; i < _emails.Count; i++)
                    {
                        if (i == 0)
                            sbEmail.Append(_emails[i]);
                        else
                            sbEmail.Append(", " + _emails[i]);
                    }
                    SetTextMsg(sbEmail.ToString());
                }
                else
                {
                    MClient client = MClient.Get(GetCtx(), GetAD_Client_ID());
                    MMailText mailtext = new MMailText(GetCtx(), GetNode().GetR_MailText_ID(), null);

                    String subject = GetNode().GetDescription()
                    + ": " + mailtext.GetMailHeader();

                    String message = mailtext.GetMailText(true)
                    + "\n-----\n" + GetNodeHelp();
                    String to = GetNode().GetEMail();

                    //client.SendEMail(to,GetNode().GetName(), subject, message, null);
                    client.SendEMail(to, GetNode().GetName(), subject, message, null);

                }
                ///////////Email//////////


                ///////////FaxEmail//////////
                GetPO(trx);
                if (_po == null)
                    throw new Exception("Persistent Object not found - AD_Table_ID="
                        + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID());
                if (_po.GetType() == typeof(DocAction) || _po.GetType().GetInterface("DocAction", true) == typeof(DocAction))
                {
                    _emails = new List<String>();
                    SendFaxEMail();
                    StringBuilder sbEmail = new StringBuilder();
                    for (int i = 0; i < _emails.Count; i++)
                    {
                        if (i == 0)
                            sbEmail.Append(_emails[i]);
                        else
                            sbEmail.Append(", " + _emails[i]);
                    }
                    SetTextMsg(sbEmail.ToString());
                }
                else
                {
                    MClient client = MClient.Get(GetCtx(), GetAD_Client_ID());
                    MMailText mailtext = new MMailText(GetCtx(), GetNode().GetR_MailText_ID(), null);

                    String subject = GetNode().GetDescription()
                    + ": " + mailtext.GetMailHeader();

                    String message = mailtext.GetMailText(true)
                    + "\n-----\n" + GetNodeHelp();
                    String to = GetNode().GetEMail();

                    //client.SendEMail(to,GetNode().GetName(), subject, message, null);
                    client.SendEMail(to, GetNode().GetName(), subject, message, null);

                }
                ///////////FaxEmail//////////


                return true;
            }
            //
            throw new ArgumentException("Invalid Action (Not Implemented) =" + action);
        }

        volatile ReportEngine re = null;
        DSProcessWorkflow.ReportInfo rep = null;
        /// <summary>
        /// Report through work order
        /// </summary>
        /// <param name="_pi"></param>
        public void ReportRun(ProcessInfo _pi)
        {
            ////start report code
            ////	Start Report	-----------------------------------------------
            //re = ReportCtl.Start(Utility.Envs.GetCtx(), _pi, false);
            //_pi.SetSummary("Report", re != null);
            //ReportCtl.Report = re;
            
            //System.Windows.Deployment.Current.Dispatcher.BeginInvoke(delegate
            //{
            //    //new PdfViewer(rep.Report, _pi.Get_AD_PrintFormat_Table_ID(), _pi).Show();
            //    //new Viewer(re);
            //});

            bool pass = false;
            var client = AEnv.GetDSPWFClient();
            client.ExecuteProcessCompleted += (ov, ev) =>
            {
                if (ev.Result != null)
                {
                    _pi = new ProcessInfo().FromList(ev.Result.ReportProcessInfo);
                    rep = ev.Result;
                }
                pass = true;

                System.Windows.Deployment.Current.Dispatcher.BeginInvoke(delegate
                {
                    new PdfViewer(rep.Report, _pi.Get_AD_PrintFormat_Table_ID(), _pi).Show();
                   // new Viewer(re);
                });

            };

            Ctx ctx = Envs.GetCtx();
            Dictionary<string, string> dic = (Dictionary<String, string>)ctx.GetMap();
            client.ExecuteProcessAsync(dic, _pi.GetAD_Process_ID(), _pi.GetTitle(), _pi.GetAD_PInstance_ID(), _pi.GetTable_ID(), _pi.GetRecord_ID());
            client.CloseAsync();

            while (!pass)
            {
                Thread.Sleep(0);
            }

            //Unlock();
 
             
        }

        /// <summary>
        /// Set Variable
        /// </summary>
        /// <param name="value">new Value</param>
        /// <param name="displayType">display type</param>
        /// <param name="textMsg">optional Message</param>
        /// <returns>true if set and throws Exception if error</returns>
        private bool SetVariable(String value, int displayType, String textMsg)
        {
            _newValue = null;
            GetPO();
            if (_po == null)
                throw new Exception("Persistent Object not found - AD_Table_ID="
                    + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID());
            //	Set Value
            Object dbValue = null;
            if (value == null)
            {
            }
            else if (displayType == DisplayType.YesNo)
                dbValue = Convert.ToBoolean("Y".Equals(value));
            else if (DisplayType.IsNumeric(displayType))
                dbValue = Convert.ToDecimal(value);
            else
                dbValue = value;
            _po.Set_ValueOfColumn(GetNode().GetAD_Column_ID(), dbValue);
            _po.Save();
            Object dbValueNew = _po.Get_ValueOfColumn(GetNode().GetAD_Column_ID());
            if (!dbValue.Equals(dbValueNew))
            {
                if (!value.Equals(dbValueNew))
                    throw new Exception("Persistent Object not updated - AD_Table_ID="
                            + GetAD_Table_ID() + ", Record_ID=" + GetRecord_ID()
                            + " - Should=" + value + ", Is=" + dbValueNew);
            }
            //	Info
            String msg = GetNode().GetAttributeName() + "=" + value;
            if (textMsg != null && textMsg.Length > 0)
                msg += " - " + textMsg;
            SetTextMsg(msg);
            _newValue = value;
            return true;
        }

        /// <summary>
        /// Set User Choice
        /// </summary>
        /// <param name="AD_User_ID">user id</param>
        /// <param name="value">new Value</param>
        /// <param name="displayType">display type</param>
        /// <param name="textMsg">optional Message</param>
        /// <returns>true if set and throws Exception if error</returns>
        public bool SetUserChoice(int AD_User_ID, String value, int displayType,
            String textMsg) //throws Exception
        {
            //	Check if user approves own document when a role is responsible
            if (GetNode().IsUserApproval() && (GetPO().GetType() == typeof(DocAction) || GetPO().GetType().GetInterface("DocAction", true) == typeof(DocAction)))
            {
                DocAction doc = (DocAction)_po;
                MUser user = new MUser(GetCtx(), AD_User_ID, null);
                MRole[] roles = user.GetRoles(_po.GetAD_Org_ID());
                bool canApproveOwnDoc = false;
                for (int r = 0; r < roles.Length; r++)
                {
                    if (roles[r].IsCanApproveOwnDoc())
                    {
                        canApproveOwnDoc = true;
                        break;
                    }	//	found a role which allows to approve own document
                }
                if (!canApproveOwnDoc)
                {
                    String info = user.GetName() + " cannot approve own document " + doc;
                    AddTextMsg(info);
                    log.Fine(info);
                    return false;		//	ignore
                }
            }

            SetWFState(StateEngine.STATE_RUNNING);
            SetAD_User_ID(AD_User_ID);
            bool ok = SetVariable(value, displayType, textMsg);
            if (!ok)
                return false;

            String newState = StateEngine.STATE_COMPLETED;
            //	Approval
            if (GetNode().IsUserApproval() && (GetPO().GetType() == typeof(DocAction) || GetPO().GetType().GetInterface("DocAction", true) == typeof(DocAction)))
            {
                DocAction doc = (DocAction)_po;
                try
                {
                    //	Not pproved
                    if (!"Y".Equals(value))
                    {
                        newState = StateEngine.STATE_ABORTED;
                        if (!(doc.ProcessIt(DocActionVariables.ACTION_REJECT)))
                            SetTextMsg("Cannot Reject - Document Status: " + doc.GetDocStatus());
                    }
                    else
                    {
                        if (IsInvoker())
                        {
                            int startAD_User_ID = GetAD_User_ID();
                            if (startAD_User_ID == 0)
                                startAD_User_ID = doc.GetDoc_User_ID();
                            int nextAD_User_ID = GetApprovalUser(startAD_User_ID,
                                doc.GetC_Currency_ID(), doc.GetApprovalAmt(),
                                doc.GetAD_Org_ID(),
                                startAD_User_ID == doc.GetDoc_User_ID());	//	own doc
                            //	No Approver
                            if (nextAD_User_ID <= 0)
                            {
                                newState = StateEngine.STATE_ABORTED;
                                SetTextMsg("Cannot Approve - No Approver");
                                doc.ProcessIt(DocActionVariables.ACTION_REJECT);
                            }
                            else if (startAD_User_ID != nextAD_User_ID)
                            {
                                ForwardTo(nextAD_User_ID, "Next Approver");
                                newState = StateEngine.STATE_SUSPENDED;
                            }
                            else	//	Approve
                            {
                                if (!(doc.ProcessIt(DocActionVariables.ACTION_APPROVE)))
                                {
                                    newState = StateEngine.STATE_ABORTED;
                                    SetTextMsg("Cannot Approve - Document Status: " + doc.GetDocStatus());
                                }
                            }
                        }
                        //	No Invoker - Approve
                        else if (!(doc.ProcessIt(DocActionVariables.ACTION_APPROVE)))
                        {
                            newState = StateEngine.STATE_ABORTED;
                            SetTextMsg("Cannot Approve - Document Status: " + doc.GetDocStatus());
                        }
                    }
                    doc.Save();
                }
                catch (Exception e)
                {
                    newState = StateEngine.STATE_TERMINATED;
                    SetTextMsg("User Choice: " + e.ToString());
                    log.Log(Level.WARNING, "", e);
                }
                //	Send Approval Notification
                if (newState.Equals(StateEngine.STATE_ABORTED))
                {
                    MClient client = MClient.Get(GetCtx(), doc.GetAD_Client_ID());
                    client.SendEMail(doc.GetDoc_User_ID(),
                        doc.GetDocumentInfo() + ": " + Utility.Msg.GetMsg(Utility.Envs.GetContext(), "NotApproved", true),
                        doc.GetSummary()
                        + "\n" + doc.GetProcessMsg()
                        + "\n" + GetTextMsg(),
                        doc.CreatePDF());
                }
            }
            SetWFState(newState);
            return ok;
        }

        /// <summary>
        /// Forward To
        /// </summary>
        /// <param name="AD_User_ID">user</param>
        /// <param name="textMsg">message</param>
        /// <returns>true if forwarded</returns>
        public bool ForwardTo(int AD_User_ID, String textMsg)
        {
            if (AD_User_ID == GetAD_User_ID())
            {
                log.Log(Level.WARNING, "Same User - AD_User_ID=" + AD_User_ID);
                return false;
            }
            //
            MUser oldUser = MUser.Get(GetCtx(), GetAD_User_ID());
            MUser user = MUser.Get(GetCtx(), AD_User_ID);
            if (user == null || user.Get_ID() == 0)
            {
                log.Log(Level.WARNING, "Does not exist - AD_User_ID=" + AD_User_ID);
                return false;
            }
            //	Update 
            SetAD_User_ID(user.GetAD_User_ID());
            SetTextMsg(textMsg);
            Save();
            //	Close up Old Event
            GetEventAudit();
            _audit.SetAD_User_ID(oldUser.GetAD_User_ID());
            _audit.SetTextMsg(GetTextMsg());
            _audit.SetAttributeName("AD_User_ID");
            _audit.SetOldValue(oldUser.GetName() + "(" + oldUser.GetAD_User_ID() + ")");
            _audit.SetNewValue(user.GetName() + "(" + user.GetAD_User_ID() + ")");
            //
            _audit.SetWFState(GetWFState());
            _audit.SetEventType(MWFEventAudit.EVENTTYPE_StateChanged);
            long ms = CommonFunctions.CurrentTimeMillis();// -_audit.GetCreated().Ticks;
            _audit.SetElapsedTimeMS(Convert.ToDecimal(ms));
            _audit.Save();
            //	Create new one
            _audit = new MWFEventAudit(this);
            _audit.Save();
            return true;
        }

        /// <summary>
        /// Set User Confirmation
        /// </summary>
        /// <param name="AD_User_ID">user</param>
        /// <param name="textMsg">optional message</param>
        public void SetUserConfirmation(int AD_User_ID, String textMsg)
        {
            log.Fine(textMsg);
            SetWFState(StateEngine.STATE_RUNNING);
            SetAD_User_ID(AD_User_ID);
            if (textMsg != null)
                SetTextMsg(textMsg);
            SetWFState(StateEngine.STATE_COMPLETED);
        }

        /// <summary>
        /// Fill Parameter
        /// </summary>
        /// <param name="pInstance">process instance</param>
        /// <param name="trx">transaction</param>
        private void FillParameter(MPInstance pInstance, Trx trx)
        {
            GetPO(trx);
            //
            MWFNodePara[] nParams = _node.GetParameters();
            MPInstancePara[] iParams = pInstance.GetParameters();
            for (int pi = 0; pi < iParams.Length; pi++)
            {
                MPInstancePara iPara = iParams[pi];
                for (int np = 0; np < nParams.Length; np++)
                {
                    MWFNodePara nPara = nParams[np];
                    if (iPara.GetParameterName().Equals(nPara.GetAttributeName()))
                    {
                        String variableName = nPara.GetAttributeValue();
                        log.Fine(nPara.GetAttributeName() + " = " + variableName);
                        //	Value - Constant/Variable
                        Object value = variableName;



                        /////////
                        String columnName = "";
                        /////////
                        if (variableName == null || (variableName != null && variableName.Length == 0))
                            value = null;
                        else if (variableName.IndexOf("@") != -1 && _po != null)	//	we have a variable
                        {

                            //	Strip
                            int index = variableName.IndexOf("@");
                            if ((index + 1) < variableName.Length && variableName[index + 1].Equals('@'))
                            {
                                iPara.SetP_String(value.ToString());
                                try
                                {
                                    if (!iPara.Save())
                                    {
                                        log.Warning("Not Saved - " + nPara.GetAttributeName());
                                    }
                                }
                                catch { }
                                break;
                            }

                            columnName = variableName.Substring(index + 1);
                            index = columnName.IndexOf("@");
                            if (index == -1)
                            {
                                log.Warning(nPara.GetAttributeName() + " - cannot evaluate=" + variableName);
                                break;
                            }
                            //int startIndx = 0;
                            //int leng = (index - startIndx) + 1;//columnName.Length;
                            //columnName = columnName.Substring(startIndx, leng);

                            //raghu
                            columnName = columnName.Substring(0, index);
                            index = _po.Get_ColumnIndex(columnName);
                            if (index != -1)
                            {
                                value = _po.Get_Value(index);
                            }
                            else	//	not a column
                            {
                                //	try Env
                                String env = GetCtx().GetContext(columnName);
                                if (env.Length == 0)
                                {
                                    log.Warning(nPara.GetAttributeName() + " - not column nor environment =" + columnName + "(" + variableName + ")");
                                    break;
                                }
                                else
                                    value = env;
                            }
                        }	//	@variable@

                        //	No Value
                        if (value == null)
                        {
                            if (nPara.IsMandatory())
                                log.Warning(nPara.GetAttributeName() + " - empty - mandatory!");
                            else
                                log.Fine(nPara.GetAttributeName() + " - empty");
                            break;
                        }

                        //	Convert to Type
                        try
                        {
                            if (DisplayType.IsNumeric(nPara.GetDisplayType())
                                || DisplayType.IsID(nPara.GetDisplayType()))
                            {
                                Decimal bd;//= null;
                                if (value.GetType() == typeof(Decimal))
                                    bd = Convert.ToDecimal(value.ToString());
                                else if (value.GetType() == typeof(int))
                                    bd = Convert.ToDecimal(value.ToString());
                                else
                                    bd = Convert.ToDecimal(value.ToString());
                                iPara.SetP_Number(bd);
                                log.Fine(nPara.GetAttributeName() + " = " + variableName + " (=" + bd + "=)");
                            }
                            else if (DisplayType.IsDate(nPara.GetDisplayType()))
                            {
                                DateTime? ts;//= null;
                                if (value.GetType() == typeof(DateTime))
                                    ts = (DateTime?)value;
                                else
                                    ts = Convert.ToDateTime(value.ToString());
                                iPara.SetP_Date(ts);
                                log.Fine(nPara.GetAttributeName() + " = " + variableName + " (=" + ts + "=)");
                            }
                            else
                            {
                                /////////
                                //iPara.SetP_String(value.ToString());
                                variableName = variableName.Replace("@" + columnName + "@", value.ToString());
                                iPara.SetP_String(variableName);
                                ////////
                                //log.Fine(nPara.GetAttributeName() + " = " + variableName
                                //    + " (=" + value + "=) " + typeof(value).FullName);
                            }
                            if (!iPara.Save())
                            {
                                log.Warning("Not Saved - " + nPara.GetAttributeName());
                            }
                        }
                        catch (Exception e)
                        {
                            //log.Warning(nPara.GetAttributeName()
                            //    + " = " + variableName + " (" + value
                            //    + ") " + typeof(value).FullName
                            //    + " - " + e.Message);
                        }
                        break;
                    }
                }	//	node parameter loop
            }	//	instance parameter loop
        }

        /// <summary>
        /// Post Immediate
        /// </summary>
        private void PostImmediate()
        {
            //if (CConnection.get().isAppsServerOK(false))
            //{
            //    try
            //    {
            //        Server server = CConnection.get().getServer();
            //        if (server != null)
            //        {
            //            String error = server.PostImmediate(Envs.getCtx(),
            //                _postImmediate.getAD_Client_ID(),
            //                _postImmediate.get_Table_ID(), _postImmediate.get_ID(),
            //                true, null);
            //            _postImmediate.get_Logger().config("Server: " + error == null ? "OK" : error);
            //            return;
            //        }
            //        else
            //            _postImmediate.get_Logger().config("NoAppsServer");
            //    }
            //    catch (RemoteException e)
            //    {
            //        _postImmediate.get_Logger().config("(RE) " + e.getMessage());
            //    }
            //    catch (Exception e)
            //    {
            //        _postImmediate.get_Logger().config("(ex) " + e.getMessage());
            //    }
            //}
        }

        /// <summary>
        /// Send EMail
        /// </summary>
        private void SendEMail()
        {
            DocAction doc = (DocAction)_po;
            MMailText text = new MMailText(GetCtx(), _node.GetR_MailText_ID(), null);
            text.SetPO(_po, true);
            //
            int tableID = _po.Get_Table_ID();
            int RecID = _po.Get_ID();
            bool isHTML = text.IsHtml();
            String subject = doc.GetDocumentInfo()
                + ": " + text.GetMailHeader();
            String message = text.GetMailText(true)
                + "\n-----\n" + doc.GetDocumentInfo()
                + "\n" + doc.GetSummary();
            FileInfo pdf = doc.CreatePDF();
            byte[] data = null;
            if (pdf != null)
            {               
                Stream stream = pdf.OpenRead();
                data = new byte[stream.Length];
                stream.Read(data, 0, data.Length);
            }
            //
            MClient client = MClient.Get(doc.GetCtx(), doc.GetAD_Client_ID());

            //	Explicit EMail
            SendEMail(client, 0, _node.GetEMail(), subject, message, data, isHTML,tableID,RecID);
            //	Recipient Type
            String recipient = _node.GetEMailRecipient();
            //	email to document user
            if (recipient == null || recipient.Length == 0)
                SendEMail(client, doc.GetDoc_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
            else if (recipient.Equals(MWFNode.EMAILRECIPIENT_DocumentBusinessPartner))
            {
                int index = _po.Get_ColumnIndex("AD_User_ID");
                if (index > 0)
                {
                    Object oo = _po.Get_Value(index);
                    if (oo.GetType() == typeof(int))
                    {
                        int AD_User_ID = int.Parse(oo.ToString());
                        if (AD_User_ID != 0)
                        {
                            SendEMail(client, AD_User_ID, null, subject, message, data, isHTML, tableID, RecID);
                        }
                        else
                        {
                            log.Fine("No User in Document");
                        }
                    }
                    else
                    {
                        log.Fine("Empty User in Document");
                    }
                }
                else
                {
                    log.Fine("No User Field in Document");
                }
            }
            else if (recipient.Equals(MWFNode.EMAILRECIPIENT_DocumentOwner))
                SendEMail(client, doc.GetDoc_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
            else if (recipient.Equals(MWFNode.EMAILRECIPIENT_WFResponsible))
            {
                MWFResponsible resp = GetResponsible();
                if (resp.IsInvoker())
                    SendEMail(client, doc.GetDoc_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
                else if (resp.IsHuman())
                    SendEMail(client, resp.GetAD_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
                else if (resp.IsRole())
                {
                    MRole role = resp.GetRole();
                    if (role != null)
                    {
                        MUser[] users = MUser.GetWithRole(role);
                        for (int i = 0; i < users.Length; i++)
                            SendEMail(client, users[i].GetAD_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
                    }
                }
                else if (resp.IsOrganization())
                {
                    MOrgInfo org = MOrgInfo.Get(GetCtx(), _po.GetAD_Org_ID(), null);
                    if (org.GetSupervisor_ID() == 0)
                    {
                        log.Fine("No Supervisor for AD_Org_ID=" + _po.GetAD_Org_ID());
                    }
                    else
                        SendEMail(client, org.GetSupervisor_ID(), null, subject, message, data, isHTML, tableID, RecID);
                }
            }
        }

        /// <summary>
        /// Send actual EMail
        /// </summary>
        /// <param name="client">client</param>
        /// <param name="AD_User_ID">user</param>
        /// <param name="email">email string</param>
        /// <param name="subject">subject</param>
        /// <param name="message">message</param>
        /// <param name="pdf">attachment</param>
        private void SendEMail(MClient client, int AD_User_ID, String email, String subject,
            String message, FileInfo pdf)
        {
            if (AD_User_ID != 0)
            {
                MUser user = MUser.Get(GetCtx(), AD_User_ID);
                email = user.GetEMail();
                //if (email != null && email.Length > 0)
                //{
                //    email = email.Trim();
                //    if (!_emails.Contains(email))
                //    {
                //        client.SendEMail(null, user, subject, message, pdf);
                //        _emails.Add(email);
                //    }
                //}
                //else
                //{
                //    log.Info("No EMail for User " + user.GetName());
                //}
                if (email.IndexOf(";") == -1)
                {
                    email = email.Trim();
                    if (!_emails.Contains(email))
                    {
                        client.SendEMail(email, null, subject, message, pdf);
                        _emails.Add(email);
                    }
                    return;
                }
                //	Multiple EMail
                StringTokenizer st = new StringTokenizer(email, ";");
                while (st.HasMoreTokens())
                {
                    String email1 = st.NextToken().Trim();
                    if (email1.Length == 0)
                        continue;
                    if (!_emails.Contains(email1))
                    {
                        client.SendEMail(email1, null, subject, message, pdf);
                        _emails.Add(email1);
                    }
                }
            }
            else if (email != null && email.Length > 0)
            {
                //	Just one
                if (email.IndexOf(";") == -1)
                {
                    email = email.Trim();
                    if (!_emails.Contains(email))
                    {
                        client.SendEMail(email, null, subject, message, pdf);
                        _emails.Add(email);
                    }
                    return;
                }
                //	Multiple EMail
                StringTokenizer st = new StringTokenizer(email, ";");
                while (st.HasMoreTokens())
                {
                    String email1 = st.NextToken().Trim();
                    if (email1.Length == 0)
                        continue;
                    if (!_emails.Contains(email1))
                    {
                        client.SendEMail(email1, null, subject, message, pdf);
                        _emails.Add(email1);
                    }
                }
            }
        }


        /// <summary>
        /// Send actual EMail
        /// </summary>
        /// <param name="client"></param>
        /// <param name="AD_User_ID"></param>
        /// <param name="email"></param>
        /// <param name="subject"></param>
        /// <param name="message"></param>
        /// <param name="pdf"></param>
        /// <param name="isHTML"></param>
        private void SendEMail(MClient client, int AD_User_ID, String email, String subject,
            String message, byte[] pdf, bool isHTML,int tableID,int RecID)
        {
            if (AD_User_ID != 0)
            {
                MUser user = MUser.Get(GetCtx(), AD_User_ID);
                email = user.GetEMail();

                if (!(MUser.NOTIFICATIONTYPE_EMail.Equals(user.GetNotificationType()) ||
                    MUser.NOTIFICATIONTYPE_EMailPlusFaxEMail.Equals(user.GetNotificationType())))
                {
                    return;
                }
                if (email == null)
                {
                    return;
                }
                //if (email != null && email.Length > 0)
                //{
                //    email = email.Trim();
                //    if (!_emails.Contains(email))
                //    {
                //        client.SendEMail(null, user, subject, message, pdf, isHTML);
                //        _emails.Add(email);
                //    }
                //}
                //else
                //{
                //    log.Info("No EMail for User " + user.GetName());
                //}
                if (email.IndexOf(";") == -1)
                {
                    email = email.Trim();
                    if (!_emails.Contains(email))
                    {
                        client.SendEMail(email, null, subject, message, pdf, isHTML,tableID,RecID);
                        _emails.Add(email);
                        return;
                    }
                    //return ;
                }
                //	Multiple EMail
                StringTokenizer st = new StringTokenizer(email, ";");
                while (st.HasMoreTokens())
                {
                    String email1 = st.NextToken().Trim();
                    if (email1.Length == 0)
                        continue;
                    if (!_emails.Contains(email1))
                    {
                        client.SendEMail(email1, null, subject, message, pdf, isHTML,tableID,RecID);
                        _emails.Add(email1);
                    }
                }
            }
            else if (email != null && email.Length > 0)
            {
                //	Just one
                if (email.IndexOf(";") == -1)
                {
                    email = email.Trim();
                    if (!_emails.Contains(email))
                    {
                        client.SendEMail(email, null, subject, message, pdf, isHTML,tableID,RecID);
                        _emails.Add(email);
                    }
                    return;
                }
                //	Multiple EMail
                StringTokenizer st = new StringTokenizer(email, ";");
                while (st.HasMoreTokens())
                {
                    String email1 = st.NextToken().Trim();
                    if (email1.Length == 0)
                        continue;
                    if (!_emails.Contains(email1))
                    {
                        client.SendEMail(email1, null, subject, message, pdf, isHTML,tableID,RecID);
                        _emails.Add(email1);
                    }
                }
            }
        }
        /// <summary>
        /// Get Process Activity (Event) History
        /// </summary>
        /// <returns>history</returns>
        public String GetHistoryHTML()
        {
            StringBuilder sb = new StringBuilder();
            MWFEventAudit[] events = MWFEventAudit.Get(GetCtx(), GetAD_WF_Process_ID());
            //SimpleDateFormat format = DisplayType.getDateFormat(DisplayType.DateTime);
            for (int i = 0; i < events.Length; i++)
            {
                MWFEventAudit audit = events[i];
                sb.Append("<p style=\"width:90%\">");
                //sb.Append("<p>");
                //sb.Append(format.format(audit.GetCreated()))
                sb.Append(audit.GetCreated().ToLongDateString())
                    .Append(" ")
                    .Append(GetHTMLpart("b", audit.GetNodeName()))
                    .Append(": ")
                    .Append(GetHTMLpart(null, audit.GetDescription()))
                    .Append(GetHTMLpart("i", audit.GetTextMsg()));
                sb.Append("</p>");
            }
            return sb.ToString();
        }

        /// <summary>
        /// Get HTML part
        /// </summary>
        /// <param name="tag">HTML tag</param>
        /// <param name="content">content</param>
        /// <returns><tag>content</tag></returns>
        private StringBuilder GetHTMLpart(String tag, String content)
        {
            StringBuilder sb = new StringBuilder();
            if (content == null || content.Length == 0)
                return sb;
            if (tag != null && tag.Length > 0)
                sb.Append("<").Append(tag).Append(">");
            sb.Append(content);
            if (tag != null && tag.Length > 0)
                sb.Append("</").Append(tag).Append(">");
            return sb;
        }

        /// <summary>
        /// Does the underlying PO (!) object have a PDF Attachment
        /// </summary>
        /// <returns>true if there is a pdf attachment</returns>
        public bool IsPdfAttachment()
        {
            if (GetPO() == null)
                return false;
            return _po.IsPdfAttachment();
        }

        /// <summary>
        /// Get PDF Attachment of underlying PO (!) object
        /// </summary>
        /// <returns>pdf data or null</returns>
        public byte[] GetPdfAttachment()
        {
            if (GetPO() == null)
                return null;
            return _po.GetPdfAttachment();
        }

        /// <summary>
        /// String Representation
        /// </summary>
        /// <returns>info</returns>
        public override String ToString()
        {
            StringBuilder sb = new StringBuilder("MWFActivity[");
            sb.Append(Get_ID()).Append(",Node=");
            if (_node == null)
                sb.Append(GetAD_WF_Node_ID());
            else
                sb.Append(_node.GetName());
            sb.Append(",State=").Append(GetWFState())
                .Append(",AD_User_ID=").Append(GetAD_User_ID())
                .Append(",").Append(GetCreated())
                .Append("]");
            return sb.ToString();
        }

        /// <summary>
        /// User String Representation.
        /// </summary>
        /// <returns></returns>
        public String ToStringX()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(GetWFStateText()).Append(": ").Append(GetNode().GetName());
            if (GetAD_User_ID() > 0)
            {
                MUser user = MUser.Get(GetCtx(), GetAD_User_ID());
                sb.Append(" (").Append(user.GetName()).Append(")");
            }
            return sb.ToString();
        }

        /// <summary>
        /// Send EMail
        /// </summary>
        private void SendFaxEMail()
        {
            DocAction doc = (DocAction)_po;
            int  tableID=_po.Get_Table_ID();
            int RecID = _po.Get_ID();
                
            MMailText text = new MMailText(GetCtx(), _node.GetR_MailText_ID(), null);
            text.SetPO(_po, true);
            bool isHTML = text.IsHtml();
            //
            String subject = doc.GetDocumentInfo()
                + ": " + text.GetMailHeader();
            String message = text.GetMailText(true)
                + "\n-----\n" + doc.GetDocumentInfo()
                + "\n" + doc.GetSummary();
            FileInfo pdf = doc.CreatePDF();
            //

            byte[] data = null;
            if (pdf != null)
            {
                Stream stream = pdf.OpenRead();
                data = new byte[stream.Length];
                stream.Read(data, 0, data.Length);
            }      
            MClient client = MClient.Get(doc.GetCtx(), doc.GetAD_Client_ID());

            //	Explicit EMail
            SendFaxEMail(client, 0, _node.GetEMail(), subject, message, data, isHTML, tableID, RecID);
            //	Recipient Type
            String recipient = _node.GetEMailRecipient();
            //	email to document user
            if (recipient == null || recipient.Length == 0)
                SendFaxEMail(client, doc.GetDoc_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
            else if (recipient.Equals(MWFNode.EMAILRECIPIENT_DocumentBusinessPartner))
            {
                int index = _po.Get_ColumnIndex("AD_User_ID");
                if (index > 0)
                {
                    Object oo = _po.Get_Value(index);
                    if (oo.GetType() == typeof(int))
                    {
                        int AD_User_ID = int.Parse(oo.ToString());
                        if (AD_User_ID != 0)
                        {
                            SendFaxEMail(client, AD_User_ID, null, subject, message, data, isHTML, tableID, RecID);
                        }
                        else
                        {
                            log.Fine("No User in Document");
                        }
                    }
                    else
                    {
                        log.Fine("Empty User in Document");
                    }
                }
                else
                {
                    log.Fine("No User Field in Document");
                }
            }
            else if (recipient.Equals(MWFNode.EMAILRECIPIENT_DocumentOwner))
                SendFaxEMail(client, doc.GetDoc_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
            else if (recipient.Equals(MWFNode.EMAILRECIPIENT_WFResponsible))
            {
                MWFResponsible resp = GetResponsible();
                if (resp.IsInvoker())
                    SendFaxEMail(client, doc.GetDoc_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
                else if (resp.IsHuman())
                    SendFaxEMail(client, resp.GetAD_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
                else if (resp.IsRole())
                {
                    MRole role = resp.GetRole();
                    if (role != null)
                    {
                        MUser[] users = MUser.GetWithRole(role);
                        for (int i = 0; i < users.Length; i++)
                            SendFaxEMail(client, users[i].GetAD_User_ID(), null, subject, message, data, isHTML, tableID, RecID);
                    }
                }
                else if (resp.IsOrganization())
                {
                    MOrgInfo org = MOrgInfo.Get(GetCtx(), _po.GetAD_Org_ID(), null);
                    if (org.GetSupervisor_ID() == 0)
                    {
                        log.Fine("No Supervisor for AD_Org_ID=" + _po.GetAD_Org_ID());
                    }
                    else
                        SendFaxEMail(client, org.GetSupervisor_ID(), null, subject, message, data, isHTML, tableID, RecID);
                }
            }
        }

        /// <summary>
        /// Send actual EMail
        /// </summary>
        /// <param name="client">client</param>
        /// <param name="AD_User_ID">user</param>
        /// <param name="email">email string</param>
        /// <param name="subject">subject</param>
        /// <param name="message">message</param>
        /// <param name="pdf">attachment</param>
        private void SendFaxEMail(MClient client, int AD_User_ID, String email, String subject,
            String message, byte[] pdf, bool isHTML,int tableID,int RecID)
        {
            if (AD_User_ID != 0)
            {
                MUser user = MUser.Get(GetCtx(), AD_User_ID);
                //email = user.GetEMail();
                if (!(MUser.NOTIFICATIONTYPE_FaxEMail.Equals(user.GetNotificationType()) ||
                    MUser.NOTIFICATIONTYPE_EMailPlusFaxEMail.Equals(user.GetNotificationType())))
                {
                    return;
                }

                email = user.GetFaxEMail();
                if (email == null)
                {
                    return;
                }
                string faxEmailDomain = user.GetFaxEMailDomain();
                string ccode = user.GetCountryCodeForMobile();
                //if (email != null && email.Length > 0)
                //{
                //    email = email.Trim();
                //    if (!_emails.Contains(email))
                //    {
                //        client.SendEMail(null, user, subject, message, pdf);
                //        _emails.Add(email);
                //    }
                //}
                //else
                //{
                //    log.Info("No EMail for User " + user.GetName());
                //}
                if (email.IndexOf(";") == -1)
                {
                    email = email.Trim();
                    if (!_emails.Contains(email))
                    {
                        if (!String.IsNullOrEmpty(faxEmailDomain))
                        {
                            email += faxEmailDomain;
                        }
                        if (!string.IsNullOrEmpty(ccode))
                        {
                            email = ccode.Trim() + email;
                        }
                        client.SendEMail(email, null, subject, message, pdf, isHTML,tableID,RecID);
                        _emails.Add(email);
                    }
                    return;
                }
                //	Multiple EMail
                StringTokenizer st = new StringTokenizer(email, ";");
                while (st.HasMoreTokens())
                {
                    String email1 = st.NextToken().Trim();
                    if (email1.Length == 0)
                        continue;
                    if (!_emails.Contains(email1))
                    {
                        if (!String.IsNullOrEmpty(faxEmailDomain))
                        {
                            email1 += faxEmailDomain;
                        }
                        if (!string.IsNullOrEmpty(ccode))
                        {
                            email1 = ccode.Trim() + email1;
                        }
                        client.SendEMail(email1, null, subject, message, pdf, isHTML, tableID, RecID);
                        _emails.Add(email1);
                    }
                }
            }
            //else if (email != null && email.Length > 0)
            //{
            //    //	Just one
            //    if (email.IndexOf(";") == -1)
            //    {
            //        email = email.Trim();
            //        if (!_emails.Contains(email))
            //        {
            //            client.SendEMail(email, null, subject, message, pdf);
            //            _emails.Add(email);
            //        }
            //        return;
            //    }
            //    //	Multiple EMail
            //    StringTokenizer st = new StringTokenizer(email, ";");
            //    while (st.HasMoreTokens())
            //    {
            //        String email1 = st.NextToken().Trim();
            //        if (email1.Length == 0)
            //            continue;
            //        if (!_emails.Contains(email1))
            //        {
            //            client.SendEMail(email1, null, subject, message, pdf);
            //            _emails.Add(email1);
            //        }
            //    }
            //}
        }

        void SendSms()
        {

            int index = _po.Get_ColumnIndex("AD_User_ID");
            if (index > 0)
            {
                Object oo = _po.Get_Value(index);
                if (oo.GetType() == typeof(int))
                {
                    int AD_User_ID = int.Parse(oo.ToString());
                    if (AD_User_ID != 0)
                    {
                        MUser user = MUser.Get(GetCtx(), AD_User_ID);
                        StringBuilder res = new StringBuilder();
                        //if (MUser.NOTIFICATIONTYPE_SMS.Equals(user.GetNotificationType()))
                        {
                            string mobile = user.GetMobile();
                            string code = user.GetCountryCodeForMobile();
                            if (mobile != null)
                            {
                                if (!String.IsNullOrEmpty(mobile.Trim()))
                                {
                                    string[] mnums = mobile.Split(';');
                                    DocAction doc = (DocAction)_po;
                                    MMailText text = new MMailText(GetCtx(), _node.GetR_MailText_ID(), null);
                                    text.SetPO(_po, true);
                                    //string msg = text.GetMailText(true) + "\n-----\n" + doc.GetDocumentInfo()
                                    //                    + "\n" + doc.GetSummary();
                                    string msg = text.GetMailText(true);
                                    res.Clear();
                                    List<string> MobileNumbers = new List<string>();
                                    for (int i = 0; i < mnums.Length; i++)
                                    {
                                        if (!string.IsNullOrEmpty(mnums[i]))
                                        {
                                            mnums[i] = mnums[i].Trim();
                                            if (mnums[i].StartsWith("0"))
                                            {
                                                mnums[i] = mnums[i].Substring(1);
                                            }
                                            if (!string.IsNullOrEmpty(code))
                                            {
                                                mnums[i] = code.Trim() + mnums[i];
                                            }
                                            //if (ValidateMobileNumber(mnums[i]))
                                            //{
                                               // res.Append(mnums[i] + "->").Append(SendSms(mnums[i], msg));
                                            if (!string.IsNullOrEmpty(mnums[i]))
                                            {
                                                MobileNumbers.Add(mnums[i]);
                                            }
                                            //}
                                            //else
                                            //{
                                            //    res.Append(mnums[i] + "->").Append("WrongMobileNumber");
                                            //}

                                        }

                                    }

                                    if (MobileNumbers.Count > 0)
                                    {
                                        var client = AEnv.GetDataServiceBasic2Client();

                                        client.SendWFSmsCompleted += (se, ev) =>
                                            {
                                                client.CloseAsync();
                                                
                                                res.Append( ev.Result);
                                                SetTextMsg(res.ToString());
                                            };
                                        client.SendWFSmsAsync(MobileNumbers, msg);
                                    }


                                }
                            }
                            SetTextMsg(res.ToString());
                        }
                    }
                    else
                    {
                        log.Fine("No User in Document");
                    }
                }
                else
                {
                    log.Fine("Empty User in Document");
                }
            }
            else
            {
                log.Fine("No User Field in Document");
            }

        }


        //public string SendSms(string mbNumber, string msg)
        //{


        //    //StringBuilder result = new StringBuilder("");

        //    IDataReader idr = null;
        //    idr = VAdvantage.DataBase.DB.ExecuteReader("select * from ad_smsconfiguration WHERE isactive='Y'");
        //    DataTable dt = new DataTable();
        //    dt.Load(idr);
        //    idr.Close();
        //    if (dt.Rows.Count == 0)
        //    {
        //        return "SmsConfigurationNotFound";
        //        //return false;
        //    }


        //    string strUrl = dt.Rows[0]["url"].ToString() + "?" + dt.Rows[0]["userKeyword"].ToString() + "=" +
        //             dt.Rows[0]["username"].ToString() + "&" + dt.Rows[0]["PasswordKeyword"].ToString() + "=" +
        //             dt.Rows[0]["password"].ToString() + "&" +
        //             dt.Rows[0]["senderKeyword"].ToString() + "=" + dt.Rows[0]["sender"].ToString() + "&" +
        //             dt.Rows[0]["messageKeyword"].ToString() + "=" +
        //             "@@Messag@@" + "&" + dt.Rows[0]["MobilenumberKeyword"].ToString() + "=" + "@@Mobile@@" + "&";
        //    strUrl += dt.Rows[0]["priorityKeyword"].ToString() + "=" + dt.Rows[0]["priorityValue"].ToString();
        //    if (dt.Rows[0]["unicodeValue"].ToString().Length > 0 || dt.Rows[0]["dndValue"].ToString().Length > 0)
        //    {
        //        strUrl += "&" + dt.Rows[0]["dndKeyword"].ToString() + "=" + dt.Rows[0]["dndValue"].ToString() +
        //            "&" + dt.Rows[0]["unicodeKeyword"].ToString() + "=" + dt.Rows[0]["unicodeValue"].ToString();
        //    }


        //    String resultmsg = string.Empty;
        //    try
        //    {
        //        string strRep = Replace(msg);
        //        string repMob = Replace(mbNumber);
        //        string uri = strUrl.Replace("@@Messag@@", strRep);
        //        uri = uri.Replace("@@Mobile@@", repMob);
        //        // uri += mbNumber;

        //        HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(uri);
        //        HttpWebResponse resp = (HttpWebResponse)webReq.GetResponse();//send sms
        //        StreamReader responseReader = new StreamReader(resp.GetResponseStream());//read the response 
        //        resultmsg = responseReader.ReadToEnd();//get result
        //        responseReader.Close();
        //        resp.Close();
        //        //  result.Append(resultmsg).Append(",");
        //    }
        //    catch (Exception e)
        //    {
        //        resultmsg = e.Message;
        //        //return false;
        //    }
        //    // return true;
        //    return resultmsg.ToString();
        //}
        private string Replace(string str)
        {
            StringBuilder replaceSpecilChar = new StringBuilder();
            replaceSpecilChar.Append(str);
            // replaceSpecilChar.Replace("%", "%25");
            replaceSpecilChar.Replace("#", "%23");
            replaceSpecilChar.Replace("$", "%24");
            replaceSpecilChar.Replace("&", "%26");
            replaceSpecilChar.Replace("+", "%2B");
            replaceSpecilChar.Replace(",", "%2C");
            replaceSpecilChar.Replace(":", "%3A");
            replaceSpecilChar.Replace(";", "%3B");
            replaceSpecilChar.Replace("=", "%3D");
            replaceSpecilChar.Replace("?", "%3F");
            replaceSpecilChar.Replace("@", "%40");
            replaceSpecilChar.Replace("<", "%3C");
            replaceSpecilChar.Replace(">", "%3E");
            replaceSpecilChar.Replace("{", "%7B");
            replaceSpecilChar.Replace("}", "%7D");
            replaceSpecilChar.Replace("|", "%7C");
            replaceSpecilChar.Replace("\\", "%5C");
            replaceSpecilChar.Replace("^", "%5E");
            replaceSpecilChar.Replace("~", "%7E");
            replaceSpecilChar.Replace("[", "%5B");
            replaceSpecilChar.Replace("]", "%5D");
            replaceSpecilChar.Replace("`", "%60");

            return replaceSpecilChar.ToString();
        }


        private bool ValidateMobileNumber(String MobileNum)
        {
            bool RtnVal = true;
            //if (MobileNum.Contains("("))
            //{
            //    MobileNum = MobileNum.Substring(0, MobileNum.ToString().IndexOf("("));
            //}

            ////Length must be >= 10 but <= 12
            if ((MobileNum.Length < 10) || (MobileNum.Length > 12))
            {
                RtnVal = false;
            }
            else
            {
                int Pos;
                int NumChars;

                NumChars = MobileNum.Length;


                //Check For Characters 
                for (Pos = 0; Pos < NumChars; Pos++)
                {

                    if (!Char.IsDigit(MobileNum[Pos]))
                    {
                        if (!char.IsWhiteSpace(MobileNum[Pos])) //Space is allowed
                        {
                            if ((MobileNum[Pos] != '(') && (MobileNum[Pos] != ')')) //( and ) is allowed
                            {
                                RtnVal = false;
                            }

                        }

                    }
                }//for

                //Check For Opening and Closing bracket

                //if (MobileNum.Contains('('))
                //{
                //    if (!MobileNum.Contains(')'))
                //    {
                //        RtnVal = false;
                //    }

                //}


            }//else
            return RtnVal;
        }

    }
}
